#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import optparse
import os
import os.path
import platform
import re
import subprocess
import sys
import tempfile

#~ Set up global variables for mplayer and avidemux.
mplayerApp = "mplayer"
avidemuxAppIndex = "avidemux2_cli"
avidemuxAppEncode = "avidemux2_cli"
if platform.platform().startswith( "Win" ):
    mplayerApp = "c:/users/juruotsa/tools/megui/tools/mencoder/mplayer.exe"
    avidemuxApp = "c:/users/juruotsa/tools/avidemux/avidemux2_cli.exe"

#~ Determine subtitle filter line for conversion script.
def getSubtitleFilter( videoFile, autoSubs ):
    # Find .idx/.sub files generated by ProjectX
    entries = os.listdir( os.path.split( os.path.abspath( videoFile ) )[0] )
    idxFound = None
    subFound = None
    for entry in entries:
        applicant = os.path.split( os.path.abspath( videoFile ) )[0] + os.path.sep + entry
        if applicant.lower().endswith( "subs.idx" ):
            idxFound = applicant
        if applicant.lower().endswith( "subs.sub" ):
            subFound = applicant
            # If both were found, return .idx file's name, else return comment.
    filterLine = ""
    if autoSubs and idxFound is not None:
        if subFound is not None:
            filterLine = 'app.video.addFilter("vobsub","subname=%s","index=0","subShift=0");' % idxFound
    return filterLine

#~ Determine autocropping.
def j264_tool_autocrop( videoFile, timeskip ):
    # Start mplayer in identify mode.
    if timeskip == -1:
        timeskip = 35
    skipSecs = 0
    idLengthCommand = mplayerApp + " -identify -frames 1 -vo null -ao null %s" % videoFile
    mplayer = subprocess.Popen( idLengthCommand, shell = True, bufsize = 128,
          stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.STDOUT, close_fds = False )
    for _line in mplayer.stdout:
        if _line.decode().startswith( "ID_LENGTH" ):
            # Set skip secs to specified percentage of video length.
            skipSecs = timeskip * int( _line.decode().strip().split( "=" )[1].split( "." )[0] ) / 100
            break
    os.waitpid( mplayer.pid, 0 )

    # Start mplayer in identify mode.
    cmd = mplayerApp + " -ss %s -identify -frames 20 -vo null -ao null -nocache -speed 100 -noframedrop -vf cropdetect=24:2 %s" % ( skipSecs, videoFile )
    mplayer = subprocess.Popen( cmd, shell = True, bufsize = 128,
          stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.STDOUT, close_fds = False )
    videoWidth = 0
    videoHeight = 0
    cropLeft = 0
    cropRight = 0
    cropTop = 0
    cropBottom = 0
    fpsValue = 0
    applicants = {}
    pat = re.compile( "\d{1,4}x\d{1,4}", re.I )
    for _line in mplayer.stdout:
        line = _line.decode().strip()
        hitIndex = line.lower().find( "crop area: x:" )
        # Dig out frame width & height from a line like the following:
        # VO: [null] 720x576 => 1024x576 Mpeg PES
        if line.startswith( "VO: " ):
            mo = pat.search( line )
            if mo is not None:
                parts = mo.group( 0 ).split( "x" )
                videoWidth = int( parts[0] )
                videoHeight = int( parts[1] )
            continue
        # Look for:
        # [CROP] Crop area: X: 0..717  Y: 71..507  (-vf crop=718:436:0:72).
        elif hitIndex != -1 and videoWidth and videoHeight:
            parts = line[hitIndex:].split()
            cropLeft = int( parts[3].split( ".." )[0] )
            cropRight = videoWidth - int( parts[3].split( ".." )[1] )
            cropTop = int( parts[5].split( ".." )[0] )
            cropBottom = videoHeight - int( parts[5].split( ".." )[1] )
            #~ Tweak cropping to even numbers
            if cropLeft & 1:
                cropLeft = cropLeft - 1
            if cropRight & 1:
                cropRight = cropRight - 1
            if cropTop & 1:
                cropTop = cropTop - 1
            if cropBottom & 1:
                cropBottom = cropBottom - 1
            key = 'app.video.addFilter("crop","left=%d","right=%d","top=%d","bottom=%d");' % ( 
                    cropLeft, cropRight, cropTop, cropBottom )
            if key in applicants:
                applicants[key] = applicants[key] + 1
            else:
                applicants[key] = 1
        elif line.startswith( "ID_VIDEO_FPS=" ):
            fpsValue = int( float( line.split( "=" )[1] ) )
    bestValue = 0
    bestKey = ''
    for key in list( applicants.keys() ):
        if applicants[key] > bestValue:
            bestValue = applicants[key]
            bestKey = key
    return [fpsValue, bestKey]

# Main.
if __name__ == "__main__":
    #~ Handle options.
    usageString = "%prog [options] input-file"
    parser = optparse.OptionParser( usage = usageString )
    parser.add_option( "-q",
                    "--quality",
                    dest = "quality",
                    help = "use QUALITY for encoding quality (one of: lapsille, puutarha)",
                    metavar = "QUALITY" )
    parser.add_option( "-o",
                    "--output-file", dest = "output",
                    help = "use FILE for output, no extension required, 'mp4' automatically appended to the name",
                    metavar = "FILE" )
    parser.add_option( "-s",
                    "--scale", dest = "scale",
                    help = "use SCALE to specify output video resolution, e.g 640:480 or 640x480; 'none' will bypass scaling",
                    metavar = "SCALE" )
    parser.add_option( "-t",
                    "--time-skip-percentage", dest = "timeskip",
                    help = "use PCT to specify autocrop check's position in percentage of video length, default is 35%",
                    default = -1,
                    type = "int",
                    metavar = "PCT" )
    parser.add_option( "-a",
                    "--auto-subs", dest = "auto_subs",
                    action = "store_true",
                    default = False,
                    help = """Flag to enable automatic subtitle file inclusion.
NOTE: you should name the sub/idx files as 'subs.sub', and 'subs.idx'.
Or make symlinks with these names into the real sub/idx files.""" )
    parser.add_option( "-k",
                    "--keep-script", dest = "keep_script",
                    action = "store_true",
                    default = False,
                    help = "Flag to control script file auto delete." )
    ( options, args ) = parser.parse_args()
    #~ Sanity checking.
    if options.output is None:
        print( ">>>Output file was not specified!", file = sys.stderr )
        parser.parse_args( ["-h"] )
    if options.quality is None:
        print( ">>>Encoding quality was not specified!", file = sys.stderr )
        parser.parse_args( ["-h"] )
    if options.scale is None:
        print( ">>>Scale not specified.", file = sys.stderr )
        parser.parse_args( ["-h"] )
    if not len( args ):
        print( ">>>No input file specified", file = sys.stderr )
        parser.parse_args( ["-h"] )

    # Autocrop.
    fpsValue, cropScriptLine = j264_tool_autocrop( args[0], options.timeskip )

    # Crop and scale only if scale is not 'none'.
    if options.scale.lower() != 'none':
        # Video scaling.
        parts = options.scale.replace( "x", ":" ).split( ":" )
        newWidth = int( parts[0] )
        newHeight = int( parts[1] )
        # Width/height must be divisible by 16.
        if newWidth % 16:
            newWidth = newWidth + ( 16 - newWidth % 16 )
        if newHeight % 16:
            newHeight = newHeight + ( 16 - newHeight % 16 )
        scaleScriptLine = 'app.video.addFilter("mpresize","w=%d","h=%d","algo=0");' % ( newWidth, newHeight )
    else:
        cropScriptLine = "//"
        scaleScriptLine = "//"

    # Get encoding script.
    scriptSkeleton = None
    try:
        scriptSkeleton = open( os.path.split( os.path.abspath( sys.argv[0] ) )[0] + os.sep + options.quality + ".js" )
    except:
        print( ">>>Unknown quality specified", file = sys.stderr )
        parser.parse_args( ["-h"] )

    # Get an absolute path to target file, since the following chdir()
    # messes up the abspath() -call into some relative directory stuff.
    targetPath = os.path.abspath( options.output )

    #~ CD to video file's directory.
    videoPath, videoFile = os.path.split( os.path.abspath( args[0] ) )
    os.chdir( videoPath )

    # Get subtitle filter.
    subtitleScriptLine = getSubtitleFilter( videoFile, options.auto_subs )

    # Save script while doing script replacements.
    scriptFile = tempfile.NamedTemporaryFile( prefix = "j264-", delete = options.keep_script )
    scriptFile.close()
    realScriptFile = open( scriptFile.name, "w" )
    #~ Write mandatory script header.
    realScriptFile.write( "//AD\n" );
    for line in scriptSkeleton:
        line = line.strip()
        #~ L I N E S    T O    P R O C E S S:
        #~ Tweak frame rate.
        if line.startswith( "app.video.setFps1000" ):
            line = "app.video.setFps1000=%d;\n" % ( 1000 * fpsValue )
        #~ Force container.
        elif line.startswith( "app.setContainer" ):
            line = "\napp.setContainer(\"MP4\");\n"
        #~ Tweak filters.
        elif line.startswith( "//** Filters **" ):
            if len( subtitleScriptLine ):
                line = subtitleScriptLine + "\n"
            else:
                line = ""
            line = line + "%s\n%s\n" % ( cropScriptLine, scaleScriptLine )
        #~ L I N E S    T O    I G N O R E:
        #~ load a video file
        #~ unnecessary assignments
        #~ audio stuff
        #~ unnecessary function calls
        #~ ignore comments and empty lines
        #~ pre-existing crop and resize filters
        elif line.split( "=" )[0] in ["app.markerA",
            "app.markerB"] or \
            line.startswith( "app.audio" ) or \
            line.startswith( "app.load" ) or \
            line.split( "(" )[0] in ["app.clearSegments",
            "app.addSegment",
            "app.rebuildIndex"] or \
            line.startswith( "//" ) or \
            line.startswith( "/*" ) or \
            len( line ) == 0 or \
            ( line.startswith( "app.video.addFilter" ) and \
            ( line.find( "\"crop\"" ) != -1 or line.find( "resize\"" ) != -1 ) ):
            continue
        else:
            line = line + "\n"
        #~ Write line into script.
        realScriptFile.write( line )
    realScriptFile.close()

    #~ Create index.
    p = subprocess.Popen( avidemuxAppIndex + " --autoindex --load %s" % videoFile, shell = True )
    sts = os.waitpid( p.pid, 0 )
    print( ">>>Index created.", file = sys.stderr )

    # Ready for run.
    p = subprocess.Popen( avidemuxAppEncode + " --load %s.idx --run %s --save %s.mp4 --quit" % ( videoFile, scriptFile.name, targetPath ), shell = True )
    sts = os.waitpid( p.pid, 0 )

    #~ Let the user know the script's location.
    if options.keep_script:
        print( ">>>Script file is '%s'." % scriptFile.name, file = sys.stderr )
